<?php


namespace app\model;

use think\Model;
//引入软删除类
use think\model\concern\SoftDelete;

class Test extends Model
{
    //定义表名，默认 模型名对应表名
    //UserType 对应 prefix_user_type
    protected $table = 'my_test';

    //定义主键
    protected $pk = 'id';

    // 设置当前模型的数据库连接
    protected $connection = 'db_config';

    //使用软删除
    use SoftDelete;
    //设置软删除的标记字段
    protected $deleteTime = 'delete_time';
    //设置软删除标记的内容（默认为null，目前好像只支持6.0.9版本）
    protected $defaultSoftDelete = 0;
    //// 软删除
    //User::destroy(1);
    //// 真实删除
    //User::destroy(1,true);

    //$user = User::find(1);
    //// 软删除
    //$user->delete();
    //// 真实删除
    //$user->force()->delete();

    //查询包含软删除的数据
    //User::withTrashed()->find();
    //User::withTrashed()->select();
    //查询仅有软删除的数据
    //User::onlyTrashed()->find();
    //User::onlyTrashed()->select();
    //恢复被软删除的数据
    //$user = User::onlyTrashed()->find(1);
    //$user->restore();

    //########## 全部配置属性
    //属性	        描述
    //name	        模型名（相当于不带数据表前后缀的表名，默认为当前模型类名）
    //table	        数据表名（默认自动获取）
    //suffix	    数据表后缀（默认为空）
    //pk	        主键名（默认为id）
    //connection	数据库连接（默认读取数据库配置）
    //query	        模型使用的查询类名称
    //field	        模型允许写入的字段列表（数组）
    //schema	    模型对应数据表字段及类型
    //type	        模型需要自动转换的字段及类型
    //strict	    是否严格区分字段大小写（默认为true）
    //disuse	    数据表废弃字段（数组）

    //支持自动写入时间戳
    //自动写入create_time和update_time
    //（全局开启） 配置文件开启自动写入时间戳字段
    //'auto_timestamp' => true, //默认字段类型为int
    //'auto_timestamp' => 'datetime', //如果不是int类型设置为datetime
    //模型单独开启，或者单独关闭
    protected $autoWriteTimestamp = true;

    // 定义时间戳字段名（如果字段非默认的名字）
    // 配置文件统一定义：'datetime_field'    =>    'create_at,update_at',
    protected $createTime = 'create_at';
    protected $updateTime = 'update_at';
    //动态关闭，如某更新操作不执行时间戳的操作
    //$user = Test::find(1);
    //$user->read +=1;
    //$user->isAutoWriteTimestamp(false)->save();

    // 设置字段信息
    protected $schema = [
        'id'          => 'int',
        'name'        => 'string',
        'status'      => 'int',
        'score'       => 'float',
        'info'        => 'string',
        'create_time' => 'datetime',
        'update_time' => 'datetime',
    ];
    //全部类型：
    //integer
    //设置为integer（整型）后，该字段写入和输出的时候都会自动转换为整型。
    //float
    //该字段的值写入和输出的时候自动转换为浮点型。
    //boolean
    //该字段的值写入和输出的时候自动转换为布尔型。
    //array
    //如果设置为强制转换为array类型，系统会自动把数组编码为json格式字符串写入数据库，取出来的时候会自动解码。
    //object
    //该字段的值在写入的时候会自动编码为json字符串，输出的时候会自动转换为stdclass对象。
    //serialize
    //指定为序列化类型的话，数据会自动序列化写入，并且在读取的时候自动反序列化。
    //json
    //指定为json类型的话，数据会自动json_encode写入，并且在读取的时候自动json_decode处理。
    //timestamp
    //可以指定字段的类型为时间戳 timestamp
    //protected $dateFormat = 'Y/m/d'; //指定时间戳格式，默认Y-m-d H:i:s
    //或直接再指定的时候定义为 timestamp:Y/m/d


    // 设置只读字段
    protected $readonly = ['name', 'email'];
    //动态方法 $user->readonly(['name','email'])->save();

    // 设置字段自动转换类型
    protected $type = [
        'score'       => 'float',
    ];

    // 设置废弃字段
    protected $disuse = [ 'status', 'type' ];

    // 设置json类型字段
    protected $json = ['info'];
    //$user = new Test;
    //$user->name = 'thinkphp';
    //$user->info = [
    //	'email'    => 'thinkphp@qq.com',
    //    'nickname '=> '流年',
    //];
    //$user->save();
    //或
    //$user = new Test;
    //$user->name = 'thinkphp';
    //$info = new \StdClass();
    //$info->email = 'thinkphp@qq.com';
    //$info->nickname = '流年';
    //$user->info = $info;
    //$user->save();

    //$user = Test::where('info->nickname','流年')->find();
    //echo $user->name; // thinkphp
    //echo $user->info->email; // thinkphp@qq.com
    //echo $user->info->nickname; // 流年

    // 设置JSON字段的类型
    protected $jsonType = [
        'info->user_id'	=>	'int'
    ];

    // 设置JSON数据返回数组
    protected $jsonAssoc = true;
    //设置后，查询代码调整为：
    //$user = Test::find(1);
    //echo $user->name; // thinkphp
    //echo $user->info['email']; // thinkphp@qq.com
    //echo $user->info['nickname']; // 流年

    //更新json数据
    //$user = Test::find(1);
    //$user->name = 'kancloud';
    //$user->info->email = 'kancloud@qq.com';
    //$user->info->nickname = 'kancloud';
    //$user->save();
    //如果设置模型的JSON数据返回数组，那么更新操作需要调整如下。
    //$user = Test::find(1);
    //$user->name = 'kancloud';
    //$info['email'] = 'kancloud@qq.com';
    //$info['nickname'] = 'kancloud';
    //$user->info = $info;
    //$user->save();


    // 模型初始化
    // init必须是静态方法，并且只在第一次实例化的时候执行，并且只会执行一次
    protected static function init()
    {
        //TODO:初始化内容
    }

    //########## 封装自定义模型方法（官方称为：查询范围）
    //使用查询范围后，只能使用find或者select查询。
    //方法格式：scope+方法名
    public function scopeTest($query)
    {
        //查询name为thinkphp的数据，只获取id，name字段
        //控制器通过Test::scope('thinkphp')->find();调用
        $query->where('name','thinkphp')->field('id,name');
    }

    public function scopeAge($query)
    {
        //查询年龄大于20的十条数据
        //Test::scope('age')->select();
        $query->where('age','>',20)->limit(10);
    }
    //还可以同时查询 查找name为thinkphp的用户并且年龄大于20的10个用户
    //Test::scope('thinkphp,age')->select();

    //###### 额外参数
    public function scopeEmail($query, $email)
    {
        $query->where('email', 'like', '%' . $email . '%');
    }

    public function scopeScore($query, $score)
    {
        $query->where('score', '>', $score);
    }
    //// 查询email包含thinkphp和分数大于80的用户
    //Test::email('thinkphp')->score(80)->select();

    // 定义全局的查询范围
    protected $globalScope = ['status'];
    //定义后该模型下每条查询都会附带scopeStatus该方法
    // 也可以动态关闭全局查询范围
    //Test::withoutGlobalScope()->select();
    // 或关闭指定的全局查询范围
    //Test::withoutGlobalScope(['status'])->select();

    //########## 获取器
    //方法格式：get+字段名（下划线转驼峰）+Attr
    //调用格式：字段名（驼峰转下划线）

    //定义了获取器之后会在下列情况自动触发：
    //模型的数据对象取值操作（$model->field_name）；
    //模型的序列化输出操作（$model->toArray()及toJson()）；
    //显式调用getAttr方法（$this->getAttr('field_name')）；

    //获取器的场景包括：
    //时间日期字段的格式化输出；
    //集合或枚举类型的输出；
    //数字状态字段的输出；
    //组合字段的输出；

    public function getStatusAttr($value)
    {
        $status = [-1=>'删除',0=>'禁用',1=>'正常',2=>'待审核'];
        return $status[$value];
        //$user = User::find(1);
        //echo $user->status; // 将会输出“正常”等转换后的结果
    }

    //可以定义不存在的字段
    public function getStatusTextAttr($value,$data) //第二个参数是当前的所有数据数组
    {
        $status = [-1=>'删除',0=>'禁用',1=>'正常',2=>'待审核'];
        return $status[$data['status']];
        //$user = User::find(1); // 获取到的数据作为$data传入获取器
        //echo $user->status_text; // 例如输出“正常”
    }

    // 设置获取器后想要获取原始数据可以使用getData方法
    // 获取原始字段数据
    //echo $user->getData('status');
    // 获取全部原始数据
    //dump($user->getData());

    //########## 修改器
    //方法格式：set+字段名（下划线转驼峰）+Attr

    //修改器的使用场景和读取器类似：
    //时间日期字段的转换写入；
    //集合或枚举类型的写入；
    //数字状态字段的写入；
    //某个字段涉及其它字段的条件或者组合写入；

    //定义了修改器之后会在下列情况下触发：
    //模型对象赋值；
    //调用模型的data方法，并且第二个参数传入true；
    //调用模型的save方法，并且传入数据；
    //显式调用模型的setAttr方法；

    //将name字段转为小写后再存入数据库
    public function setNameAttr($value)
    {
        return strtolower($value);
    }

    public function setTestFieldAttr($value, $data)
    {
        $this->set('other_field', $data['some_field']);
        //上面的例子，在test_field字段的修改器中修改了other_field字段数据，并且没有返回值（表示不对test_field字段做任何修改）。
    }
    //批量修改
    //$user = new User();
    //$data['name'] = 'THINKPHP';
    //$data['email'] = 'thinkphp@qq.com';
    //$user->data($data, true); //第二个参数设为true
    //$user->save();
    //echo $user->name; // thinkphp
    //或
    //$user = new User();
    //$data['name'] = 'THINKPHP';
    //$data['email'] = 'thinkphp@qq.com';
    //$user->save($data);
    //echo $user->name; // thinkphp

    //########## 模型事件
    //用于关联操作数据库，或记录日志等操作
    //事件	            描述	    事件方法名
    //after_read	    查询后	onAfterRead
    //before_insert	    新增前	onBeforeInsert
    //after_insert	    新增后	onAfterInsert
    //before_update	    更新前	onBeforeUpdate
    //after_update	    更新后	onAfterUpdate
    //before_write	    写入前	onBeforeWrite
    //after_write	    写入后	onAfterWrite
    //before_delete	    删除前	onBeforeDelete
    //after_delete	    删除后	onAfterDelete
    //before_restore	恢复前	onBeforeRestore
    //after_restore	    恢复后	onAfterRestore

    public static function onBeforeUpdate($user)
    {
        if ('thinkphp' == $user->name) {
            return false;
        }
    }

    public static function onAfterDelete($user)
    {
        //这里通常是其他模型的操作，当前数据删除后，与其相关的模型也执行删除操作，但通常用事务操作
        Test::destroy($user->id);
    }

    //########## 模型关联
    //模型方法	        关联类型
    //hasOne	        一对一
    //belongsTo	        一对一
    //hasMany	        一对多
    //hasOneThrough	    远程一对一
    //hasManyThrough	远程一对多
    //belongsToMany	    多对多
    //morphMany	        多态一对多
    //morphOne	        多态一对一
    //morphTo	        多态

    //类型	    关联关系	        相对的关联关系
    //一对一	    hasOne	        belongsTo
    //一对多	    hasMany	        belongsTo
    //多对多	    belongsToMany	belongsToMany
    //远程一对多	hasManyThrough	不支持
    //多态一对一	morphOne	    morphTo
    //多态一对多	morphMany	    morphTo

    //格式（这是一种规范而非必须）：方法名驼峰写法，首字母小写
    public function userProfile()
    {
        //必须事先定义好userProfile模型，这里只做演示
        return $this->hasOne(UserProfile::class);
        //控制器调用
        //$user->userProfile;
        //$user->user_profile; // 建议使用
        //$user->user_profile->mobile; // 将会查询UserProfile模型的mobile字段
    }

    //一对一
    //hasOne('关联模型类名', '外键', '主键');
    //除了关联模型外，其它参数都是可选。
    //关联模型（必须）：关联模型类名
    //外键：默认的外键规则是当前模型名（不含命名空间，下同）+_id ，例如user_id
    //主键：当前模型主键，默认会自动获取也可以指定传入
    //关联查询
    //// 查询用户昵称是think的用户
    //// 注意第一个参数是关联方法名（不是关联模型名）
    //$users = Test::hasWhere('profile', ['nickname'=>'think'])->select();
    //
    //// 可以使用闭包查询
    //$users = Test::hasWhere('profile', function($query) {
    //	$query->where('nickname', 'like', 'think%');
    //})->select();

    //$users = Test::with(['profile'	=> function($query) {
    //	$query->field('id,user_id,name,email');
    //}])->select();
    //foreach ($users as $user) {
    //	echo $user->profile->name;
    //}

    //$users = User::withJoin('profile', 'LEFT')->select(); //默认是inner join，第二个参数可以设置为左或右join
    //foreach ($users as $user) {
    //	echo $user->profile->name;
    //}

    //关联更新/插入
    //$user = Test::find(1);
    //// 如果关联表中还没有数据 则进行新增
    //$user->profile()->save(['email' => 'thinkphp']);

    //$user = User::find(1);
    //$user->profile->email = 'thinkphp';
    //$user->profile->save();
    //// 或者
    //$user->profile->save(['email' => 'thinkphp']);

    //由于关联涉及知识点过多，且其实都差不多，因此本源码中省略后面的关联知识，详细请查看文档

}